Skip to content

Skip post-warp-sync wait in parachain bootstrap#3246

Open
lrubasze wants to merge 6 commits intomainfrom
lrubasze/notify_warp_synced_finalized
Open

Skip post-warp-sync wait in parachain bootstrap#3246
lrubasze wants to merge 6 commits intomainfrom
lrubasze/notify_warp_synced_finalized

Conversation

@lrubasze
Copy link
Copy Markdown
Contributor

@lrubasze lrubasze commented May 7, 2026

Problem

After a relay-chain warp sync, the runtime_service places the warp-synced block directly as the outer finalized_block — it never enters the inner async_tree, so subscribers never receive Block/Finalized notifications for it. fetch_parachain_head_from_relay waits for the next Notification::Finalized, which only fires when the relay finalizes the block after the warp-synced one (~6–12 s).

Fix

Reshape the warp-sync MustSubscribe path so the warp-synced block flows through the normal OutputUpdate::BlockOutputUpdate::Finalized sequence:

  • Previous finalized block (pre_warp_finalized) becomes the tree's initial finalized state — a tree-level predecessor only, not the real chain parent.
  • Warp-synced block is inserted as a non-finalized child with its runtime pre-completed (no actual download), then input_finalize'd.
  • Subscribers receive pre_warp_finalized as initial finalized, then Block(warp_synced) + Finalized(warp_synced) on the channel back-to-back.

pre_warp_finalized is pruned the moment warp-synced is finalized; its placeholder runtime is Err(CodeNotFound).

This PR replaces #3210

@lrubasze lrubasze changed the title Lrubasze/notify warp synced finalized runtime_service: notify subscribers of warp-synced finalized block May 7, 2026
@lrubasze lrubasze requested review from bkchr, gab8i and skunert May 7, 2026 09:29
@lrubasze lrubasze changed the title runtime_service: notify subscribers of warp-synced finalized block Skip post-warp-sync wait in parachain bootstrap May 7, 2026
@lrubasze
Copy link
Copy Markdown
Contributor Author

lrubasze commented May 7, 2026

Benchmark results

Paseo + Asset Hub Paseo, time-to-finalized, n=10 iterations.

Mode Baseline main branch f4f2b69e (median / p95 / max) PR f361eab2 (median / p95 / max) Speedup (median / p95)
Cold 11.4s / 56.4s / 59.1s 5.5s / 9.8s / 12.3s 2.1× / 5.8×
Warm 17.8s / 91.0s / 92.9s 4.1s / 7.9s / 9.0s 4.3× / 11.5×

Highlights:

  • Warm variance collapses: σ 30.0s → 1.7s.
  • Cold variance: σ 18.2s → 2.2s; worst run 12.3s vs baseline 59.1s.
  • Baseline warm had 1/10 run fail to finalize; PR has 10/10.

Copy link
Copy Markdown
Member

@bkchr bkchr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have merged these two branches, can you explain on what is the difference to before? Why is this now sending the required events.

Comment thread lib/src/chain/async_tree.rs Outdated
Comment thread light-base/src/runtime_service.rs Outdated
Comment thread light-base/src/runtime_service.rs Outdated
Comment on lines +1491 to +1511
if use_pre_warp_finalized {
// Pre-complete the runtime so the tree-advance loop emits
// `Block` + `Finalized` without an actual download.
let warp_synced_idx = new_tree.input_insert_block(
warp_synced_block.clone(),
None,
false,
true,
);
let async_op = match new_tree
.next_necessary_async_op(&background.platform.now())
{
async_tree::NextNecessaryAsyncOp::Ready(op) => op,
async_tree::NextNecessaryAsyncOp::NotReady { .. } => unreachable!(
"warp-synced block just inserted with pending async op"
),
};
debug_assert_eq!(async_op.block_index, warp_synced_idx);
new_tree.async_op_finished(async_op.id, runtime);
new_tree.input_finalize(warp_synced_idx);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what this changes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I wanted here is to:

  • attach the warp-synced runtime to the warp_synced_block
  • mark the block as the next finalized

Not sure if this is fine to set same_async_op_as_parent = true, because it would implicate using the finalized_async_user_data we used to build the tree, which I deliberately set as Err(RuntimeError::CodeNotFound), because there is no actual runtime that could be associated with pre_warp_finalized block (there is no guarantee that pre_warp_finalized block uses the same runtime as warp_synced_block).
We could use warp-synced runtime as finalized_async_user_data when building a tree and then we could use approach suggested by you. Though not sure about the case when someone invokes runtime_call against pre_warp_finalized block and its runtime is supposed to be different than for warp_synced_block.
If this is a case, then better to return RuntimeError::CodeNotFound.

@bkchr
Copy link
Copy Markdown
Member

bkchr commented May 8, 2026

Don't we just need to set this to true? Then we don't need your next_necessary_async_op call?

@lrubasze
Copy link
Copy Markdown
Contributor Author

lrubasze commented May 8, 2026

Don't we just need to set this to true? Then we don't need your next_necessary_async_op call?

#3246 (comment)

@lrubasze
Copy link
Copy Markdown
Contributor Author

lrubasze commented May 8, 2026

You have merged these two branches, can you explain on what is the difference to before? Why is this now sending the required events.

Not sure what branches you mean. Is this about the difference between this PR and #3210 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants